using System;
using System.Collections.Generic;
using System.Text;

namespace Msrp.Message
{
    public class BufferParser
    {
        public static void parseBuffer(ref Buffer sp)
        {
            if (false == sp.isChunkContinuation)
            {
                // First of all try to find the first line
                int startIdx = findFirstLine(sp.dataBuffer, sp.reseivedBytes);
                if (-1 < startIdx)
                {
                    sp.begginigOfChunkIdx = startIdx;

                    // Found start of the message chunk, get the transaction id 
                    int transacIdStartIdx = startIdx + 5;
                    if (transacIdStartIdx >= sp.reseivedBytes)
                    {
                        // TODO: handle the case when "MSRP" string is the last thing in the buffer
                    }

                    Byte space = (Byte)' ';
                    int transacIdEndIdx = Array.FindIndex<Byte>(sp.dataBuffer, transacIdStartIdx, sp.reseivedBytes - transacIdStartIdx, space.Equals);

                    if (transacIdEndIdx > transacIdStartIdx)
                    {
                        sp.startTransactionId = Encoding.ASCII.GetString(sp.dataBuffer, transacIdStartIdx, transacIdEndIdx - transacIdStartIdx);
                    }
                }
            }

            // Request line MUST terminate with \r\n
            int elIdx = findEndLine(sp.dataBuffer, sp.begginigOfChunkIdx, sp.reseivedBytes);
            // Now try to find the seven hyphens, that terminates the body
            if (-1 < elIdx)
            {
                // TODO: Handle the case when 7 hyphens not followed by transac id and termChar
                // in this case it's not the chunk termination
                int sevenHyphensIdx = findSevenHyphens(sp.dataBuffer, elIdx, sp.reseivedBytes);
                if (-1 < sevenHyphensIdx)
                {
                    // Now let's try to find end of line, transaction id and termination char
                    elIdx = findEndLine(sp.dataBuffer, sevenHyphensIdx, sp.reseivedBytes);
                    if (-1 < elIdx)
                    {
                        sp.endTransactionId = Encoding.ASCII.GetString(sp.dataBuffer, sevenHyphensIdx + 6, elIdx - (sevenHyphensIdx + 6));
                        sp.termChar = (char)sp.dataBuffer[elIdx - 1];
                        sp.endOfChunkIdx = (elIdx - 1);
                    }
                }
            }
        }


        private static int findSevenHyphens(byte[] buff, int searchStartIdx, int searchEndIdx)
        {
            bool sevenHyphensFound = false;
            Byte hyphen = (Byte)'-';
            int firstHyphenIdx = -1;
            do
            {
                firstHyphenIdx = Array.FindIndex<Byte>(buff, searchStartIdx, searchEndIdx - searchStartIdx, hyphen.Equals);
                if (firstHyphenIdx != -1)
                {
                    bool hyphenSeries = true;
                    for (int i = firstHyphenIdx; i < (firstHyphenIdx + 6); i++)
                    {
                        hyphenSeries &= (buff[i] == '-');
                        if (hyphenSeries == false)
                        {
                            break;
                        }
                    }
                    if (true == hyphenSeries)
                    {
                        sevenHyphensFound = true;
                        return firstHyphenIdx;
                    }
                }
            } while (firstHyphenIdx != -1 || false == sevenHyphensFound);
            return -1;
        }

        private static int findEndLine(byte[] buff, int searchStartIdx, int searchEndIdx)
        {
            Byte cr = (Byte)'\r';
            Byte lf = (Byte)'\n';
            int crIdx = -1;
            do
            {
                crIdx = Array.FindIndex<Byte>(buff, searchStartIdx, searchEndIdx - searchStartIdx, cr.Equals);
                if (searchEndIdx > crIdx && buff[crIdx + 1] == lf)
                {
                    return crIdx;
                }
            } while (crIdx != -1);
            return -1;
        }

        private static int findFirstLine(byte[] buff, int searchEndIdx)
        {
            for (int i = 0; i < searchEndIdx - 3; i++)
            {
                if (buff[i] == 'M' &&
                    buff[i + 1] == 'S' &&
                    buff[i + 2] == 'R' &&
                    buff[i + 3] == 'P')
                {
                    return i;
                }
            }
            return -1;
        }

        public class Buffer
        {
            public byte[] dataBuffer = new byte[2048];
            public int reseivedBytes = 0;
            public int begginigOfChunkIdx = -1;
            public int endOfChunkIdx = -1;
            public char termChar = '\0';
            public string startTransactionId = "";
            public string endTransactionId = "";
            public bool isChunkContinuation = false;
        };

    }
}
